package quota

import (
	"sync"
	"sync/atomic"
	"time"

	"go-common/library/rate/limit/quota/rate"
)

var (
	allowers               = make(map[string]Allower)
	allowerLock            sync.RWMutex
	_defaultAllowerRefSecs = int64(5)
)

type Allower interface {
	Allow() bool
	AllowN(n int) bool
	// 是否为注册资源
	UnknowResource() bool
	Close()
}

type allower struct {
	*resource
	notAllowStranger bool
}

type AllowerConfig struct {
	ID               string
	Disable          bool
	NotAllowStranger bool
}

func NewAllower(cfg *AllowerConfig) (l Allower) {
	if cfg == nil {
		panic("allower config is nil")
	}
	if cfg.Disable {
		return &disabledAllower{}
	}
	allowerLock.Lock()
	defer allowerLock.Unlock()
	l = allowers[cfg.ID]
	if l == nil {
		l = newAllower(cfg.ID, cfg.NotAllowStranger)
		allowers[cfg.ID] = l
	}
	return
}

func newAllower(id string, notAllowStranger bool) (w *allower) {
	r := &resource{
		id:              id,
		refreshInterval: _defaultAllowerRefSecs,
		closCh:          make(chan bool),
		lm:              rate.NewLimiter(rate.Inf, 1),
		updateCh:        make(chan float64, 1),
	}
	w = &allower{
		resource:         r,
		notAllowStranger: notAllowStranger,
	}
	go r.tickerproc()
	go r.syncerproc()
	go r.refreshResource(1, 0)
	return
}

func (l *allower) Allow() bool {
	return l.AllowN(1)
}

func (l *allower) AllowN(n int) bool {
	if l.unknow && l.notAllowStranger {
		return false
	}
	atomic.AddInt64(&l.requests[l.idx()], int64(n))
	allow := l.lm.AllowN(time.Now(), n)
	if allow {
		atomic.AddInt64(&l.used[l.idx()], int64(n))
	}
	return allow
}

func (l *allower) UnknowResource() bool {
	return l.unknow
}

type disabledAllower struct{}

func (l *disabledAllower) Allow() bool          { return true }
func (l *disabledAllower) AllowN(n int) bool    { return true }
func (l *disabledAllower) Close()               {}
func (l *disabledAllower) UnknowResource() bool { return false }
